Update to curl 0.3
authorAlex Crichton <alex@alexcrichton.com>
Wed, 18 May 2016 17:01:40 +0000 (10:01 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Sun, 5 Jun 2016 06:48:05 +0000 (23:48 -0700)
Cargo.lock
Cargo.toml
src/cargo/lib.rs
src/cargo/ops/registry.rs
src/cargo/sources/registry.rs
src/cargo/util/errors.rs
src/crates-io/Cargo.toml
src/crates-io/lib.rs

index aab3cb77dd61b500ab4af5320508c221f591ee7f..860297bbd9f45625de08f542635b9b496ea24a63 100644 (file)
@@ -6,15 +6,14 @@ dependencies = [
  "bufstream 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "crates-io 0.2.0",
  "crossbeam 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "curl 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "curl-sys 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
+ "curl 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "docopt 0.6.78 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "flate2 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "fs2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "git2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "git2-curl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "git2-curl 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "hamcrest 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -72,7 +71,7 @@ dependencies = [
 name = "crates-io"
 version = "0.2.0"
 dependencies = [
- "curl 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "curl 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -84,19 +83,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "curl"
-version = "0.2.19"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "curl-sys 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
+ "curl-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "openssl-sys 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "curl-sys"
-version = "0.1.34"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "gcc 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -178,10 +175,10 @@ dependencies = [
 
 [[package]]
 name = "git2-curl"
-version = "0.4.1"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "curl 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "curl 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "git2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
index 6560c9ee320aad00d8b2d5ae3ffcd2519e49d98f..2b0c4acbb269186f521aa0212aeb9687907d85c8 100644 (file)
@@ -20,19 +20,18 @@ path = "src/cargo/lib.rs"
 advapi32-sys = "0.1"
 crates-io = { path = "src/crates-io", version = "0.2" }
 crossbeam = "0.2"
-curl = "0.2"
-curl-sys = "0.1"
+curl = "0.3"
 docopt = "0.6"
 env_logger = "0.3"
 filetime = "0.1"
 flate2 = "0.2"
 fs2 = "0.2"
 git2 = "0.4"
-git2-curl = "0.4"
+libgit2-sys = "0.4"
+git2-curl = "0.5"
 glob = "0.2"
 kernel32-sys = "0.2"
 libc = "0.2"
-libgit2-sys = "0.4"
 log = "0.3"
 num_cpus = "0.2"
 regex = "0.1"
index c20b150dfcda5ef40c945ea0b1cc8d8dd1282c9e..45676e2ef998fd2c7d87920dc836982cefabf0a5 100644 (file)
@@ -6,7 +6,6 @@
 extern crate crates_io as registry;
 extern crate crossbeam;
 extern crate curl;
-extern crate curl_sys;
 extern crate docopt;
 extern crate filetime;
 extern crate flate2;
index 4b4358e12b1e6e10da9af0ef7414e4409ea30bea..49764c67482b169377934510872f3afb5cd5917a 100644 (file)
@@ -3,8 +3,9 @@ use std::env;
 use std::fs::{self, File};
 use std::iter::repeat;
 use std::path::{Path, PathBuf};
+use std::time::Duration;
 
-use curl::http;
+use curl::easy::Easy;
 use git2;
 use registry::{Registry, NewCrate, NewCrateDependency};
 use term::color::BLACK;
@@ -156,24 +157,22 @@ pub fn registry(config: &Config,
 }
 
 /// Create a new HTTP handle with appropriate global configuration for cargo.
-pub fn http_handle(config: &Config) -> CargoResult<http::Handle> {
+pub fn http_handle(config: &Config) -> CargoResult<Easy> {
     // The timeout option for libcurl by default times out the entire transfer,
     // but we probably don't want this. Instead we only set timeouts for the
     // connect phase as well as a "low speed" timeout so if we don't receive
     // many bytes in a large-ish period of time then we time out.
-    let handle = http::handle().timeout(0)
-                               .connect_timeout(30_000 /* milliseconds */)
-                               .low_speed_limit(10 /* bytes per second */)
-                               .low_speed_timeout(30 /* seconds */);
-    let handle = match try!(http_proxy(config)) {
-        Some(proxy) => handle.proxy(proxy),
-        None => handle,
-    };
-    let handle = match try!(http_timeout(config)) {
-        Some(timeout) => handle.connect_timeout(timeout as usize)
-                               .low_speed_timeout((timeout as usize) / 1000),
-        None => handle,
-    };
+    let mut handle = Easy::new();
+    try!(handle.connect_timeout(Duration::new(30, 0)));
+    try!(handle.low_speed_limit(10 /* bytes per second */));
+    try!(handle.low_speed_time(Duration::new(30, 0)));
+    if let Some(proxy) = try!(http_proxy(config)) {
+        try!(handle.proxy(&proxy));
+    }
+    if let Some(timeout) = try!(http_timeout(config)) {
+        try!(handle.connect_timeout(Duration::new(timeout as u64, 0)));
+        try!(handle.low_speed_time(Duration::new(timeout as u64, 0)));
+    }
     Ok(handle)
 }
 
index 614d6540a608032ad0d24e0c78630434db12a03f..06e6344bb96ef5087892d8d18472bc4c62f92e40 100644 (file)
@@ -164,7 +164,7 @@ use std::io::SeekFrom;
 use std::io::prelude::*;
 use std::path::{PathBuf, Path};
 
-use curl::http;
+use curl::easy::Easy;
 use flate2::read::GzDecoder;
 use git2;
 use rustc_serialize::hex::ToHex;
@@ -188,7 +188,7 @@ pub struct RegistrySource<'cfg> {
     cache_path: Filesystem,
     src_path: Filesystem,
     config: &'cfg Config,
-    handle: Option<http::Handle>,
+    handle: Option<Easy>,
     hashes: HashMap<(String, String), String>, // (name, vers) => cksum
     cache: HashMap<String, Vec<(Summary, bool)>>,
     updated: bool,
@@ -300,24 +300,34 @@ impl<'cfg> RegistrySource<'cfg> {
                 self.handle.as_mut().unwrap()
             }
         };
-        // TODO: don't download into memory (curl-rust doesn't expose it)
-        let resp = try!(handle.get(url.to_string()).follow_redirects(true).exec());
-        if resp.get_code() != 200 && resp.get_code() != 0 {
-            return Err(internal(format!("failed to get 200 response from {}\n{}",
-                                        url, resp)))
+        // TODO: don't download into memory, but ensure that if we ctrl-c a
+        //       download we should resume either from the start or the middle
+        //       on the next time
+        try!(handle.get(true));
+        try!(handle.url(&url.to_string()));
+        try!(handle.follow_location(true));
+        let mut state = Sha256::new();
+        let mut body = Vec::new();
+        {
+            let mut handle = handle.transfer();
+            try!(handle.write_function(|buf| {
+                state.update(buf);
+                body.extend_from_slice(buf);
+                Ok(buf.len())
+            }));
+            try!(handle.perform());
+        }
+        let code = try!(handle.response_code());
+        if code != 200 && code != 0 {
+            bail!("failed to get 200 response from `{}`, got {}", url, code)
         }
 
         // Verify what we just downloaded
-        let actual = {
-            let mut state = Sha256::new();
-            state.update(resp.get_body());
-            state.finish()
-        };
-        if actual.to_hex() != expected_hash {
+        if state.finish().to_hex() != expected_hash {
             bail!("failed to verify the checksum of `{}`", pkg)
         }
 
-        try!(dst.write_all(resp.get_body()));
+        try!(dst.write_all(&body));
         try!(dst.seek(SeekFrom::Start(0)));
         Ok(dst)
     }
index b7c03c21045b386e24b86449227021636a460c20..a31fffc04c170f18ced86e83eb159b14d6312678 100644 (file)
@@ -8,7 +8,6 @@ use std::str;
 use std::string;
 
 use curl;
-use curl_sys;
 use git2;
 use rustc_serialize::json;
 use semver;
@@ -302,17 +301,13 @@ impl NetworkError for git2::Error {
         }
     }
 }
-impl NetworkError for curl::ErrCode {
+impl NetworkError for curl::Error {
     fn maybe_spurious(&self) -> bool {
-        match self.code()  {
-            curl_sys::CURLcode::CURLE_COULDNT_CONNECT |
-            curl_sys::CURLcode::CURLE_COULDNT_RESOLVE_PROXY |
-            curl_sys::CURLcode::CURLE_COULDNT_RESOLVE_HOST |
-            curl_sys::CURLcode::CURLE_OPERATION_TIMEDOUT |
-            curl_sys::CURLcode::CURLE_RECV_ERROR
-            => true,
-            _ => false
-        }
+        self.is_couldnt_connect() ||
+            self.is_couldnt_resolve_proxy() ||
+            self.is_couldnt_resolve_host() ||
+            self.is_operation_timedout() ||
+            self.is_recv_error()
     }
 }
 
@@ -334,7 +329,7 @@ from_error! {
     git2::Error,
     json::DecoderError,
     json::EncoderError,
-    curl::ErrCode,
+    curl::Error,
     CliError,
     toml::Error,
     url::ParseError,
@@ -360,7 +355,7 @@ impl CargoError for io::Error {}
 impl CargoError for git2::Error {}
 impl CargoError for json::DecoderError {}
 impl CargoError for json::EncoderError {}
-impl CargoError for curl::ErrCode {}
+impl CargoError for curl::Error {}
 impl CargoError for ProcessError {}
 impl CargoError for CargoTestError {}
 impl CargoError for CliError {}
index 72f732295796916c8058438d2341563ca1e16f5f..951523617c9af534a81dcb21d3f1978de61e51e0 100644 (file)
@@ -13,6 +13,6 @@ name = "crates_io"
 path = "lib.rs"
 
 [dependencies]
-curl = "0.2"
+curl = "0.3"
 url = "1.0"
 rustc-serialize = "0.3"
index 10ab68a728f87c30a459fa39fc74afd7f87a1701..8ecb932fbdee2208570b741e5be2227d7c9d0ff1 100644 (file)
@@ -9,9 +9,7 @@ use std::io::prelude::*;
 use std::io::{self, Cursor};
 use std::result;
 
-use curl::http;
-use curl::http::handle::Method::{Put, Get, Delete};
-use curl::http::handle::{Method, Request};
+use curl::easy::{Easy, List};
 use rustc_serialize::json;
 
 use url::percent_encoding::{percent_encode, QUERY_ENCODE_SET};
@@ -19,7 +17,7 @@ use url::percent_encoding::{percent_encode, QUERY_ENCODE_SET};
 pub struct Registry {
     host: String,
     token: Option<String>,
-    handle: http::Handle,
+    handle: Easy,
 }
 
 pub type Result<T> = result::Result<T, Error>;
@@ -31,8 +29,8 @@ pub enum Auth {
 }
 
 pub enum Error {
-    Curl(curl::ErrCode),
-    NotOkResponse(http::Response),
+    Curl(curl::Error),
+    NotOkResponse(u32, Vec<String>, Vec<u8>),
     NonUtf8Body,
     Api(Vec<String>),
     Unauthorized,
@@ -55,6 +53,12 @@ impl From<json::DecoderError> for Error {
     }
 }
 
+impl From<curl::Error> for Error {
+    fn from(err: curl::Error) -> Error {
+        Error::Curl(err)
+    }
+}
+
 #[derive(RustcDecodable)]
 pub struct Crate {
     pub name: String,
@@ -109,11 +113,12 @@ pub struct User {
 
 impl Registry {
     pub fn new(host: String, token: Option<String>) -> Registry {
-        Registry::new_handle(host, token, http::Handle::new())
+        Registry::new_handle(host, token, Easy::new())
     }
 
-    pub fn new_handle(host: String, token: Option<String>,
-                      handle: http::Handle) -> Registry {
+    pub fn new_handle(host: String,
+                      token: Option<String>,
+                      handle: Easy) -> Registry {
         Registry {
             host: host,
             token: token,
@@ -177,12 +182,17 @@ impl Registry {
             Some(s) => s,
             None => return Err(Error::TokenMissing),
         };
-        let request = self.handle.put(url, &mut body)
-            .content_length(size)
-            .header("Accept", "application/json")
-            .header("Authorization", &token);
-        let response = handle(request.exec());
-        let _body = try!(response);
+        try!(self.handle.put(true));
+        try!(self.handle.url(&url));
+        try!(self.handle.in_filesize(size as u64));
+        let mut headers = List::new();
+        try!(headers.append("Accept: application/json"));
+        try!(headers.append(&format!("Authorization: {}", token)));
+        try!(self.handle.http_headers(headers));
+
+        let _body = try!(handle(&mut self.handle, &mut |buf| {
+            body.read(buf).unwrap_or(0)
+        }));
         Ok(())
     }
 
@@ -190,7 +200,7 @@ impl Registry {
         let formated_query = percent_encode(query.as_bytes(), QUERY_ENCODE_SET);
         let body = try!(self.req(
             format!("/crates?q={}&per_page={}", formated_query, limit),
-            None, Get, Auth::Unauthorized
+            None, Auth::Unauthorized
         ));
 
         let crates = try!(json::decode::<Crates>(&body));
@@ -212,51 +222,75 @@ impl Registry {
     }
 
     fn put(&mut self, path: String, b: &[u8]) -> Result<String> {
-        self.req(path, Some(b), Put, Auth::Authorized)
+        try!(self.handle.put(true));
+        self.req(path, Some(b), Auth::Authorized)
     }
 
     fn get(&mut self, path: String) -> Result<String> {
-        self.req(path, None, Get, Auth::Authorized)
+        try!(self.handle.get(true));
+        self.req(path, None, Auth::Authorized)
     }
 
     fn delete(&mut self, path: String, b: Option<&[u8]>) -> Result<String> {
-        self.req(path, b, Delete, Auth::Authorized)
+        try!(self.handle.custom_request("DELETE"));
+        self.req(path, b, Auth::Authorized)
     }
 
-    fn req(&mut self, path: String, body: Option<&[u8]>,
-           method: Method, authorized: Auth) -> Result<String> {
-        let mut req = Request::new(&mut self.handle, method)
-                              .uri(format!("{}/api/v1{}", self.host, path))
-                              .header("Accept", "application/json")
-                              .content_type("application/json");
+    fn req(&mut self,
+           path: String,
+           body: Option<&[u8]>,
+           authorized: Auth) -> Result<String> {
+        try!(self.handle.url(&format!("{}/api/v1{}", self.host, path)));
+        let mut headers = List::new();
+        try!(headers.append("Accept: application/json"));
+        try!(headers.append("Content-Type: application/json"));
 
         if authorized == Auth::Authorized {
             let token = match self.token.as_ref() {
                 Some(s) => s,
                 None => return Err(Error::TokenMissing),
             };
-            req = req.header("Authorization", &token);
+            try!(headers.append(&format!("Authorization: {}", token)));
         }
+        try!(self.handle.http_headers(headers));
         match body {
-            Some(b) => req = req.body(b),
-            None => {}
+            Some(mut body) => {
+                try!(self.handle.upload(true));
+                try!(self.handle.in_filesize(body.len() as u64));
+                handle(&mut self.handle, &mut |buf| body.read(buf).unwrap_or(0))
+            }
+            None => handle(&mut self.handle, &mut |_| 0),
         }
-        handle(req.exec())
     }
 }
 
-fn handle(response: result::Result<http::Response, curl::ErrCode>)
-          -> Result<String> {
-    let response = try!(response.map_err(Error::Curl));
-    match response.get_code() {
+fn handle(handle: &mut Easy,
+          read: &mut FnMut(&mut [u8]) -> usize) -> Result<String> {
+    let mut headers = Vec::new();
+    let mut body = Vec::new();
+    {
+        let mut handle = handle.transfer();
+        try!(handle.read_function(|buf| Ok(read(buf))));
+        try!(handle.write_function(|data| {
+            body.extend_from_slice(data);
+            Ok(data.len())
+        }));
+        try!(handle.header_function(|data| {
+            headers.push(String::from_utf8_lossy(data).into_owned());
+            true
+        }));
+        try!(handle.perform());
+    }
+
+    match try!(handle.response_code()) {
         0 => {} // file upload url sometimes
         200 => {}
         403 => return Err(Error::Unauthorized),
         404 => return Err(Error::NotFound),
-        _ => return Err(Error::NotOkResponse(response))
+        code => return Err(Error::NotOkResponse(code, headers, body))
     }
 
-    let body = match String::from_utf8(response.move_body()) {
+    let body = match String::from_utf8(body) {
         Ok(body) => body,
         Err(..) => return Err(Error::NonUtf8Body),
     };
@@ -275,8 +309,15 @@ impl fmt::Display for Error {
         match *self {
             Error::NonUtf8Body => write!(f, "response body was not utf-8"),
             Error::Curl(ref err) => write!(f, "http error: {}", err),
-            Error::NotOkResponse(ref resp) => {
-                write!(f, "failed to get a 200 OK response: {}", resp)
+            Error::NotOkResponse(code, ref headers, ref body) => {
+                try!(writeln!(f, "failed to get a 200 OK response, got {}", code));
+                try!(writeln!(f, "headers:"));
+                for header in headers {
+                    try!(writeln!(f, "    {}", header));
+                }
+                try!(writeln!(f, "body:"));
+                try!(writeln!(f, "{}", String::from_utf8_lossy(body)));
+                Ok(())
             }
             Error::Api(ref errs) => {
                 write!(f, "api errors: {}", errs.join(", "))